#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <pthread.h>

/* 	Calculating the pi value using borwein's algorithm (1984)	
	http://en.wikipedia.org/wiki/Borwein's_algorithm                
	ATTENTION: we strongly recommend to see the sequential version of this code first (borwein-seq.c)	*/

/* 	these funcions is used by each of the threads, which calculates the operations	*/

//a1 = (sqrt(a) + 1.0/sqrt(a))/2.0;
void * op1(void * args)
{
	double * args_d = (double *) args;
	args_d[3] = (sqrt(args_d[0]) + 1.0/sqrt(args_d[0]))/2.0;
	
	/* the function must return something - NULL will do */
	return NULL;
}

//b1 = ((1.0 + b)*sqrt(a))/(a + b);
void * op2(void * args)
{
	double * args_d = (double *) args;	
	args_d[4] = ((1.0 + args_d[1])*sqrt(args_d[0]))/(args_d[0] + args_d[1]);

	return NULL;
}

//p1 = ((1.0 + a1)*p*b1)/(1.0 + b1);
void * op3(void * args)
{
	double * args_d = (double *) args;
	args_d[5] = ((1.0 + args_d[3])*args_d[2]*args_d[4])/(1.0 + args_d[4]);

	return NULL;
}

int main(int argc, char ** argv)
{
	if(argc != 2) {
		printf("Usage: ./borwein-par <number_of_iteractions>\n");
		exit(1);
	}

	/* 	initializating the variables	*/

	//	number of iteractions
	int n = atoi(argv[1]);	

	//	all terms used by the borwein's method
	double a = sqrt(2), b = 0.0, p = 2.0 + sqrt(2);
	double a1, b1, p1;
	pthread_t pt1;
	pthread_t pt2;
	pthread_t pt3;
	
	double args[6];

	// how the vector represents our variables of the sequential version
	args[0] = a;
	args[1] = b;
	args[2] = p;
	args[3] = a1;
	args[4] = b1;
	args[5] = p1;

	int i;
	for(i = 0; i < n; i++) {

		//	for each iteraction calculate all new terms of the sequence

		// 	create the threads that can run in paralel (these operations are independents)
		if(pthread_create(&pt1, NULL, op1, args))
			fprintf(stderr, "Error creating thread 1\n"), exit(1);
		if(pthread_create(&pt2, NULL, op2, args))
			fprintf(stderr, "Error creating thread 2\n"), exit(1);


		//	waits for the first and second thread to finish, so we can procede the calculation
		if(pthread_join(pt1, NULL))
			fprintf(stderr, "Error joining thread 1\n"), exit(1);
		if(pthread_join(pt2, NULL))
			fprintf(stderr, "Error joining thread 2\n"), exit(1);

		
		//      create the last thread (for the three operation) which depends of the 'a1' term (operation/thread 1)
		//	and 'b1' term (operation/thread 2)
		if(pthread_create(&pt3, NULL, op3, args))
			fprintf(stderr, "Error creating thread 3\n"), exit(1);

		
		// waits for the rest of the threads
		if(pthread_join(pt3, NULL))
			fprintf(stderr, "Error joining thread 3\n"), exit(1);

		//	a = a1, b = b1, p = p1;
		args[0] = args[3];
		args[1] = args[4];
		args[2] = args[5];
	}
		
	//	at the end print p that represents the value of Pi
	printf("%.7lf\n", args[2]);

	return 0;
}
